/**
 * Framework for batch testing user profile models
 * Final project by Sergey Nepomnyachiy and Julia Polchin
 * Supervisor: Tsvi Kuflik
 *
 */
package models.collaborative;

import java.util.Vector;

import core.environment.Singleton;

/**
 * Class for handling very naive collaborative filtering
 * 
 * @author Sergey and Julia
 */
@Singleton
public class Collaborative {
	// single instance of Collaborative class
	private static Collaborative instance = new Collaborative();

	private int k = 0; // the k of KNN used in prediction algorithm
	private KNN neighbourhood; // encapsulates 'users X items' matrix

	private Collaborative() {
		this.neighbourhood = new KNN(new Vector<Vector<Double>>(), this.k);
	}

	/**
	 * Standard method for getting an instance of a signleton
	 * 
	 * @return single instance of Collaborative class
	 */
	public static Collaborative getInstance() {
		return instance;
	}

	/**
	 * Adding new user's ratings as a row in 'users X items' matrix
	 * 
	 * @param ratings
	 *            vector of doubles representing the rating of items
	 */
	public void addNeighbour(Vector<Double> ratings) {
		this.neighbourhood.addNeighbour(ratings);
	}
	
	/**
	 * Adding new user's ratings as a row in 'users X items' matrix
	 * 
	 * @param ratings
	 *            vector of doubles representing the rating of items
	 */
	public void addNeighbour(Vector<Double> ratings, Vector<Integer> indices) {
		for (Integer i : indices)
			ratings.setElementAt(0.0, i);
		
		this.neighbourhood.addNeighbour(ratings);
	}

	/**
	 * Used to pad ratings vector with zeroes.
	 * 
	 * @param ratings
	 *            vector with user ratings
	 * @param size
	 *            the width of 'users X items' matrix
	 * @return legal row in 'users X items' matrix
	 */
	@SuppressWarnings("boxing")
	private Vector<Double> padding(Vector<Double> ratings, int size) {
		if (ratings.size() >= size)
			return ratings;

		Vector<Double> tmp = ratings;
		if (ratings.size() < size) {
			tmp = new Vector<Double>(size);
			tmp.addAll(ratings);
			for (int i = 0; i <= (size - ratings.size()); ++i)
				tmp.add(0.0);
		}

		return tmp;
	}

	/**
	 * Using already known ratings of a user predicts his opinion of other items
	 * 
	 * @param ratings
	 *            ratings for some of the items
	 * @return predicted rating for all items in system
	 */
	public Vector<Double> predictRatings(Vector<Double> ratings) {
		int size = this.neighbourhood.getItemsCount();
		Vector<Double> tmp = ratings;
		if (ratings.size() < size)
			tmp = padding(ratings, size);
		return this.neighbourhood.getNearestOne(tmp);
	}

	/**
	 * Used to init. the 'users X items' matrix
	 */
	public void clearAll() {
		this.neighbourhood.clearAll();
	}

	/**
	 * Setter for the k parameter
	 * 
	 * @param k
	 *            the k of KNN algorithm
	 */
	@SuppressWarnings("boxing")
	public void setK(Integer k) {
		this.k = k;
		this.neighbourhood.setK(k);
	}
}
